package com.dosgi;

import java.io.File;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLDecoder;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.StringTokenizer;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.dosgi.classloader.ContextFinder;
import com.dosgi.classloader.DosgiClassLoader;
import com.dosgi.clazz.ClassHandler;
import com.dosgi.kit.ManiFestKit;
import com.dosgi.kit.StrKit;
import com.dosgi.kit.ThreadPoolKit;
import com.dosgi.module.IModuleContext;
import com.dosgi.module.ModuleContext;

/**
 * 代理引导程序
 * 
 * @author dingnate
 *
 */
public class DosgiLauncher {
	private static transient final Logger LOG = LoggerFactory
			.getLogger(DosgiLauncher.class);
	private static final String MODULE_SCAN_ROOT = "Module-Scan-Root";
	private static final String MODULE_HOME = System.getProperty("module.home",
			System.getProperty("user.dir") + "/modules");
	// blockOnLaunch为false时后台启动模块，否则当前线程启动模块且当前线程阻塞至LifeCycle状态设为CLOSED
	private static boolean blockOnLaunch = true;

	/**
	 * 入口
	 * 
	 * @param args
	 * @param inst
	 */
	public static void main(String args) {
		LOG.info("dosgi start...");
		File moduleHome = new File(MODULE_HOME);
		if (moduleHome.exists()) {
			try {
				// 获取模块
				Collection<IModuleContext> moduleContexts = getModules(moduleHome);
				// 初始化模块
				initModules(moduleContexts);
				// 扫描模块类
				scanModuleClasses(moduleContexts);
				// 启动模块
				if (blockOnLaunch) {
					// 当前线程启动模块
					startModules(moduleContexts);
					Dosgi.getLifeCycle().waitForClosed();
				} else {
					// 后台线程启动模块
					startModulesBackground(moduleContexts);
				}
			} catch (Exception e) {
				LOG.error("dosgi start failed.", e);
			}
		} else
			LOG.error(
					"dosgi start failed.\nCaused by module home:{} not exist",
					MODULE_HOME);
	}

	private static void scanModuleClasses(
			Collection<IModuleContext> moduleContexts) {
		LOG.info("start scan module classes...");
		Set<ClassHandler> classHandlers = Dosgi.context().getClassHandlers();
		for (IModuleContext mContext : moduleContexts) {
			String filePath = mContext.getFilePath();
			ClassLoader cl = mContext.getClassLoader();
			String moduleScanRoot = StrKit.blankDefault(mContext
					.getManifestAttributes().get(MODULE_SCAN_ROOT), "");
			JarFile jarFile = null;
			try {
				Class<?> clazz = null;
				filePath = URLDecoder.decode(filePath, "UTF-8");
				jarFile = new JarFile(filePath);
				Enumeration<JarEntry> entries = jarFile.entries();
				while (entries.hasMoreElements()) {
					JarEntry jarEntry = entries.nextElement();
					String entryName = jarEntry.getName();
					// 限定一下包路径，否则有性能问题，或者根据模块的配置文件来读指定包路径，先临时解决了
					if (!jarEntry.isDirectory()
							&& entryName.startsWith(moduleScanRoot)
							&& entryName.endsWith(".class")) {
						String className = entryName.replace("/", ".")
								.substring(0, entryName.length() - 6);
						try {
							clazz = Class.forName(className, false, cl);
						} catch (Throwable ex) {
							// ignore
						}
						if (clazz != null) {
							// 处理class逻辑
							for (ClassHandler classHandler : classHandlers) {
								try {
									classHandler.handle(clazz);
								} catch (Exception e) {
									LOG.error(
											"handle class failed. class:"
													+ className
													+ ",handler:"
													+ classHandler.getClass()
															.getName(), e);
								}
							}
						}
					}
				}
				LOG.info("scan module {} classes success.",
						mContext.getSymbolicName());
			} catch (IOException e1) {
				LOG.info("scan module " + mContext.getSymbolicName()
						+ " classes success.", e1);
			} finally {
				if (jarFile != null)
					try {
						jarFile.close();
					} catch (IOException e) {
					}
			}

		}

	}

	// 获取模块
	private static Collection<IModuleContext> getModules(File moduleHome) {
		LOG.info("start get modules in module home:{}...", MODULE_HOME);
		LinkedList<IModuleContext> mContexts = new LinkedList<IModuleContext>();
		File[] moduleFiles = moduleHome.listFiles();
		ModuleContext systemMContext = null;
		for (File moduleFile : moduleFiles) {
			if (moduleFile.getName().endsWith(".jar")) {
				try {
					Map<String, String> attrs = ManiFestKit
							.getMainAttributes(new JarFile(moduleFile));
					ModuleContext mContext = new ModuleContext(attrs,
							moduleFile.getAbsolutePath());
					// 初始化模块类加载器
					mContext.setClassLoader(new DosgiClassLoader(mContext,
							getClassPathURLs(mContext.getFilePath()),
							ModuleContext.class.getClassLoader()));
					if ("System".equals(attrs.get("Phase")))
						systemMContext = mContext;
					else
						mContexts.add(mContext);
					LOG.info("get module from {} success.",
							moduleFile.getName());
				} catch (Exception e) {
					LOG.error("error occured when get module from "
							+ moduleFile.getName(), e);
				}
			}
		}
		// 根据模块依赖关系排序，底层模块在前，上层模块在后
		sortModules(mContexts);
		// 系统模块放在首位,优先启动
		if (systemMContext != null)
			mContexts.addFirst(systemMContext);
		return mContexts;
	}

	/**
	 * 根据模块依赖关系排序，底层模块在前，上层模块在后
	 * 
	 * @param mContexts
	 */
	private static void sortModules(LinkedList<IModuleContext> mContexts) {
		if (mContexts.isEmpty())
			return;
		HashSet<IModuleContext> passed = new HashSet<IModuleContext>();
		for (int i = 0; i < mContexts.size(); i++)
			pushRequiredForward(mContexts.get(i), mContexts, passed);
	}

	/**
	 * 把模块依赖的模块推到前面
	 * 
	 * @param mContext
	 * @param mContexts
	 * @param passed
	 */
	private static void pushRequiredForward(IModuleContext mContext, LinkedList<IModuleContext> mContexts,
			Collection<IModuleContext> passed) {
		if (passed.contains(mContext))
			return;
		passed.add(mContext);
		Collection<IModuleContext> resolvedRequireModulesWithImport = mContext.getResolvedRequireModulesWithImport();
		if (resolvedRequireModulesWithImport.isEmpty()) {
			if (mContexts.getFirst() == mContext)
				return;
			mContexts.remove(mContext);
			mContexts.addFirst(mContext);
			return;
		}
		int index = mContexts.indexOf(mContext);
		for (IModuleContext rMContext : resolvedRequireModulesWithImport) {
			int indexR = mContexts.indexOf(rMContext);
			if (indexR > index) {
				mContexts.remove(indexR);
				mContexts.add(index, rMContext);
			}
			pushRequiredForward(rMContext, mContexts, passed);
		}
	}

	// 初始化模块
	private static void initModules(Collection<IModuleContext> moduleContexts) {
		Dosgi.getLifeCycle().setState(LifeCycleState.INITING);
		LOG.info("start init modules...");
		for (IModuleContext mContext : moduleContexts) {
			ContextFinder contextFinder = new ContextFinder(
					mContext.getClassLoader());
			try {
				// 设置线程上下文类加载器
				contextFinder.setContextClassLoader();
				mContext.init();
				LOG.info("init module {} success.", mContext.getSymbolicName());
			} catch (Exception e) {
				LOG.error("init module " + mContext.getSymbolicName()
						+ " failed.", e);
			} finally {
				// 设置线程上下文类加载器
				contextFinder.revertContextClassLoader();
			}
		}
		LOG.info("init modules success.");
		Dosgi.getLifeCycle().setState(LifeCycleState.INITED);
	}

	// 后台启动模块
	private static void startModulesBackground(
			final Collection<IModuleContext> moduleContexts) {
		ThreadPoolKit.submit(new Runnable() {
			@Override
			public void run() {
				startModules(moduleContexts);
			}
		});
	}

	// 启动模块
	private static void startModules(Collection<IModuleContext> moduleContexts) {
		Dosgi.getLifeCycle().setState(LifeCycleState.STARTING);
		LOG.info("start modules...");
		for (IModuleContext mContext : moduleContexts) {
			ContextFinder contextFinder = new ContextFinder(
					mContext.getClassLoader());
			try {
				// 设置线程上下文类加载器
				contextFinder.setContextClassLoader();
				mContext.start();
				LOG.info("start module {} success.", mContext.getSymbolicName());
			} catch (Exception e) {
				LOG.error("start module " + mContext.getSymbolicName()
						+ " failed.", e);
			} finally {
				// 设置线程上下文类加载器
				contextFinder.revertContextClassLoader();
			}
		}
		LOG.info("start modules success.");
		Dosgi.getLifeCycle().setState(LifeCycleState.STARTED);
	}

	/**
	 * 获取ClassLoaderUrl
	 * 
	 * @param classPath
	 * @return
	 * @throws MalformedURLException
	 */
	@SuppressWarnings("deprecation")
	public static URL[] getClassPathURLs(String classPath)
			throws MalformedURLException {
		List<URL> staticUrls = new ArrayList<URL>();
		StringTokenizer tokenizer = new StringTokenizer(classPath, ",;");
		while (tokenizer.hasMoreTokens()) {
			String res = tokenizer.nextToken().trim();
			File file = new File(res);

			// 目录则遍历子jar包
			if (file.isDirectory()) {
				File[] childFiles = file.listFiles();
				for (File childFile : childFiles) {
					if (childFile.getName().endsWith(".jar")) {
						staticUrls.add(file.toURL());
					}
				}
			}
			staticUrls.add(file.toURL());

		}
		return staticUrls.toArray(new URL[staticUrls.size()]);
	}

	/**
	 * @return the blockOnLaunch
	 */
	public static final boolean isBlockOnLaunch() {
		return blockOnLaunch;
	}

	/**
	 * @param blockOnLaunch
	 *            the blockOnLaunch to set
	 */
	public static final void setBlockOnLaunch(boolean blockOnLaunch) {
		DosgiLauncher.blockOnLaunch = blockOnLaunch;
	}
}
